package com.xbang.core.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Simple utility class for converting different types
 * 
 * @author Carl
 * 
 */
public abstract class TypeConverterUtils {

	private static final long serialVersionUID = -177551230439661351L;

	private static final Log logger = LogFactory.getLog(TypeConverterUtils.class);

	/**
	 * Maps between primitive types and their wrappers and vice versa.
	 */
	private static final Map<Class<?>, Class<?>> PRIMITIVE_COUNTERPARTS;

	private static final List<Class<?>> ACCEPTABLE_PRIMITIVE_TYPES;

	static {

		Map<Class<?>, Class<?>> primitiveToWrapper = new HashMap<Class<?>, Class<?>>() {

			private static final long serialVersionUID = -177551230439661351L;

			{
				put(int.class, Integer.class);
				put(long.class, Long.class);
				put(boolean.class, Boolean.class);
				put(byte.class, Byte.class);
				put(short.class, Short.class);
				put(float.class, Float.class);
				put(double.class, Double.class);
				put(char.class, Character.class);
			}
		};

		Map<Class<?>, Class<?>> counterparts = new HashMap<Class<?>, Class<?>>();
		for (Map.Entry<Class<?>, Class<?>> entry : primitiveToWrapper.entrySet()) {
			Class<?> key = entry.getKey();
			Class<?> value = entry.getValue();
			counterparts.put(key, value);
			counterparts.put(value, key);
		}

		PRIMITIVE_COUNTERPARTS = Collections.unmodifiableMap(counterparts);

		List<Class<?>> acceptablePrimitiveTypes = new ArrayList<Class<?>>();
		acceptablePrimitiveTypes.addAll(PRIMITIVE_COUNTERPARTS.keySet());
		acceptablePrimitiveTypes.add(String.class);
		acceptablePrimitiveTypes.add(Date.class);
		acceptablePrimitiveTypes.add(BigDecimal.class);
		ACCEPTABLE_PRIMITIVE_TYPES = Collections.unmodifiableList(acceptablePrimitiveTypes);
	}

	public static String ConventParseString(Class type, Object value) {
		if (type.equals(Date.class)) {
			return DateUtils.format((Date) value, "yyyy-MM-dd");
		}
		return value.toString();
	}

	/**
	 * convert Primitive type to the wrapper type
	 * 
	 * @param value
	 * @param type
	 * @return
	 */
	public static Object parse(String value, Class<?> type) {

		if (value == null)
			return null;

		if (type.isPrimitive()) {
			try {
				Class<?> wrapper = PRIMITIVE_COUNTERPARTS.get(type);
				final Method parser = wrapper.getMethod("parse" + StringUtils.capitalize(type.getName()), String.class);

				// do manual boxing here for primitive type
				return parser.invoke(null, value);

			} catch (NoSuchMethodException e) {
				throw new AssertionError(e);
			} catch (IllegalAccessException e) {
				throw new AssertionError(e);
			} catch (IllegalArgumentException e) {
				throw new AssertionError(e);
			} catch (InvocationTargetException e) {
				throw new RuntimeException(e);
			}
		} else {
			throw new AssertionError("Not Supported Type: " + type);
		}

	}

	/**
	 * 
	 * @param clazz
	 * @return
	 */

	@SuppressWarnings("unchecked")
	public static boolean isAcceptablePrimitiveType(Class clazz) {
		return ACCEPTABLE_PRIMITIVE_TYPES.contains(clazz) || (clazz.isEnum());
	}

}
